import java.io.File;
import org.w3c.dom.*;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.InputStreamReader;

import java.util.*;

/**
 * The class to read configuration files (config.xml) specified for this environment
 *
 */
public class Configuration 
{
	public static String DBPath;
	public static String QueryPath;
	public static String ProcessedQueryPath;
	public static Integer dimension;
	public static Integer projectionDimension;				// Number of target dimension for Random Projection
	public static Integer k;								// define k for k-NN
	public static Integer numberOfPoints;					// Used to limit how many documents from the file to be read as Database
	public static Integer numberOfQueries;					// Used to limit how many documents from the file to be processed as query
	public static Engine.QUERY_TYPE individualQueries[];	// Used if runAllQuery is false
	public static Boolean runAllQuery;						
	private static ArrayList<Engine.QUERY_TYPE> queryList;
	public static Point.DIST_TYPE type;
	public static String ResultPath;
	
	/**
	 * 
	 * @param item the node to be read. It always contains two attributes : key and value. It parses to corresponding key
	 */
	private static void readConfig(Node item)
	{
		NamedNodeMap attributes = item.getAttributes();
		Node keyAttr = attributes.getNamedItem("key");
		Node valueAttr = attributes.getNamedItem("value");
		String key = keyAttr.getNodeValue();
		String value = valueAttr.getNodeValue();
		
		if (key.equalsIgnoreCase("DBPath")) DBPath = value;
		else if (key.equalsIgnoreCase("QueryPath")) QueryPath = value;
		else if (key.equalsIgnoreCase("ProcessedQueryPath")) ProcessedQueryPath = value;
		else if (key.equalsIgnoreCase("queryType")) 
		{
			if (value.equalsIgnoreCase("euclidean")) type = Point.DIST_TYPE.EUCLIDEAN;
			else if (value.equalsIgnoreCase("cos_sim")) type = Point.DIST_TYPE.COS_SIM;
		}
		else if (key.equalsIgnoreCase("dimension")) dimension = Integer.parseInt(value);
		else if (key.equalsIgnoreCase("projectionDimension")) projectionDimension = Integer.parseInt(value);
		else if (key.equalsIgnoreCase("k")) k = Integer.parseInt(value);
		else if (key.equalsIgnoreCase("numberOfPoints")) numberOfPoints = Integer.parseInt(value);
		else if (key.equalsIgnoreCase("numberOfQueries")) numberOfQueries = Integer.parseInt(value);
		else if (key.equalsIgnoreCase("runAllQuery")) 
		{
			runAllQuery = (value.toLowerCase().equalsIgnoreCase("true")) ? true : false;
		}
		else if (key.equalsIgnoreCase("individualQuery"))
		{
			if (value.equalsIgnoreCase("exhaustive")) queryList.add(Engine.QUERY_TYPE.EXHAUSTIVE);
			else if (value.equalsIgnoreCase("random_projection")) queryList.add(Engine.QUERY_TYPE.RANDOM_PROJECTION);
			else if (value.equalsIgnoreCase("simple_vp")) queryList.add(Engine.QUERY_TYPE.SIMPLE_VP);
			else if (value.equalsIgnoreCase("rp_tree")) queryList.add(Engine.QUERY_TYPE.RP_TREE);
		}
		else if (key.equalsIgnoreCase("ResultPath")) ResultPath = value;
	}
	
	/**
	 * Parse the XML configuration file using java built-in XML parser
	 * and the keys are contained within <configuration> tag
	 * @param fileName File name of the XML configuration file
	 */
	public static void loadConfiguration(String fileName)
	{
		try
		{
			FileInputStream fin = new FileInputStream(new File(fileName));
			BufferedReader is = new BufferedReader(new InputStreamReader(fin));
			StringBuilder content = new StringBuilder();
			String line = null;
			while ((line = is.readLine()) != null) content.append(line + "\n");
			
			ByteArrayInputStream bs = new ByteArrayInputStream(content.toString().getBytes());
			DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
			Document doc = docBuilder.parse(new org.xml.sax.InputSource(bs));
			
			fin.close();
			is.close();
			bs.close();
			
			NodeList listOfElements = doc.getElementsByTagName("configuration");
			int len = listOfElements.getLength();
			if (len == 0) 
			{
				System.out.println("There's no <configuration> element");
				System.exit(1);
			}
			
			Node context = listOfElements.item(0);
			Node config = context.getFirstChild();
			
			if (config == null) return;
			
			queryList = new ArrayList<Engine.QUERY_TYPE>();
			if (config.getNodeType() == Node.ELEMENT_NODE) readConfig(config);
			
			while((config = config.getNextSibling()) != null) 
			{
				if (config.getNodeType() == Node.ELEMENT_NODE) readConfig(config);
			}
			
			individualQueries = queryList.toArray(new Engine.QUERY_TYPE[queryList.size()]);
			
			File testDir = new File(DBPath);
			if (!testDir.isDirectory()) {System.out.println("DBPath has to be a accessable directory"); System.exit(1);}
			testDir = new File(QueryPath);
			if (!testDir.isDirectory()) {System.out.println("QueryPath has to be a accessable directory"); System.exit(1);}
			testDir = new File(ProcessedQueryPath);
			if (!testDir.isDirectory()) {System.out.println("ProcessedQueryPath has to be a accessable directory"); System.exit(1);}
			testDir = new File(ResultPath);
			if (!testDir.isDirectory()) {System.out.println("ResultPath has to be a accessable directory"); System.exit(1);}
		}
		catch (Exception ex)
		{
			ex.printStackTrace();
			System.out.println("Failed to load config file");
			System.exit(1);
		}
	}
}
